﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace STSdb.MTL
{
    public static class CachedReflections
    {
        static ReaderWriterLockSlim _sync_cachedReflections = new ReaderWriterLockSlim();
        static Dictionary<string, object> _cachedReflections = new Dictionary<string, object>();

        static CachedReflections()
        {
            Init();
        }

        public static void Init()
        {

            _sync_cachedReflections.EnterWriteLock();
            try
            {
                Type ex = typeof(STSdb.MTL.Transaction);
                //**** Adding type for Transaction class
                //_cachedReflections.Add("Type_Transaction", ex);

                //**** Adding MethodInfo for Select command of the Transaction class.
                //Method info for select
                //System.Reflection.MethodInfo mi = ex.GetMethods().Where(r => r.ToString().Equals("STSdb.MTL.TableRow`2[T,T1] Select[T,T1](System.String, STSdb.MTL.eSelectMode, T)")).First();
                System.Reflection.MethodInfo mi = ex.GetMethods().Where(r => r.ToString().EndsWith("Select[T,T1](System.String, STSdb.MTL.eSelectMode, T)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/Select", mi);

                //**** Adding MethodInfo for SelectForward command of the Transaction class.
                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("SelectForward[T,T1](System.String, STSdb.MTL.eSelectMode)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/SelectForward", mi);

                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("SelectForward[T,T1](System.String, STSdb.MTL.eSelectMode, T)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/SelectForwardStartKey", mi);

                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("SelectForward[T,T1](System.String, STSdb.MTL.eSelectMode, T, T)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/SelectForwardStartEndKey", mi);

                //*** SelectBackward
                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("SelectBackward[T,T1](System.String, STSdb.MTL.eSelectMode)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/SelectBackward", mi);

                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("SelectBackward[T,T1](System.String, STSdb.MTL.eSelectMode, T)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/SelectBackwardLastKey", mi);

                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("SelectBackward[T,T1](System.String, STSdb.MTL.eSelectMode, T, T)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/SelectBackwardLastFirstKey", mi);

                //*** Count
                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("Count[T,T1](System.String, Boolean)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/CountWithPossibleLocking", mi);

                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("MaxRow[T,T1](System.String, Boolean)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/MaxRowWithPossibleLocking", mi);

                mi = ex.GetMethods().Where(r => r.ToString().EndsWith("MinRow[T,T1](System.String, Boolean)", StringComparison.OrdinalIgnoreCase)).First();
                _cachedReflections.Add("Transaction/Method/MinRowWithPossibleLocking", mi);

            }
            finally
            {
                _sync_cachedReflections.ExitWriteLock();
            }          

        }

        /// <summary>
        /// Gets cached object by name
        /// </summary>
        /// <param name="objectName"></param>
        /// <returns></returns>
        public static object GetObject(string objectName)
        {
            object o;

            _sync_cachedReflections.EnterReadLock();
            try
            {
                _cachedReflections.TryGetValue(objectName, out o);
            }
            finally
            {
                _sync_cachedReflections.ExitReadLock();
            }

            
            return o;
        }


        /// <summary>
        /// <para>Fast Reflection is used</para>
        /// Gets value from supplied object instance, using type as schema and propertyName as string.
        /// Also, caches the reflection type for future usages.
        /// <para>Method contains no try/catch</para>
        /// <para>STSdb.MTL.CachedReflections.Get_FastPropertyAccessor(tr, tbrCloseType, "Value");</para>
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static object Get_FastPropertyAccessor(object instance, Type type, string propertyName)
        {
            return FindFastPropertyAccessor(type, propertyName).Get(instance);
        }


        /// <summary>
        /// <para>Fast Reflection is used</para>
        /// Sets value for supplied object instance, using type as schema and propertyName as string.
        /// Also, caches the reflection type for future usages.
        /// <para>Method contains no try/catch</para>
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static void Set_FastPropertyAccessor(object instance, Type type, string propertyName, object value)
        {
            FindFastPropertyAccessor(type, propertyName).Set(instance,value);
        }

        /// <summary>
        /// Searches Fast reflection type in cache. Creates if not found.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        private static STSdb.MTL.Utils.Reflection.FastDynamicPropertyAccessor.PropertyAccessor FindFastPropertyAccessor(Type type, string propertyName)
        {
            object o;
            string typePropertyDefinition = "Type/" + type.ToString() + "/" + propertyName;
            STSdb.MTL.Utils.Reflection.FastDynamicPropertyAccessor.PropertyAccessor fastProperty;

            _sync_cachedReflections.EnterUpgradeableReadLock();
            try
            {
                _cachedReflections.TryGetValue(typePropertyDefinition, out o);

                if (o == null)
                {
                    _sync_cachedReflections.EnterWriteLock();
                    try
                    {
                        _cachedReflections.TryGetValue(typePropertyDefinition, out o);
                        if (o == null)
                        {
                            fastProperty = new STSdb.MTL.Utils.Reflection.FastDynamicPropertyAccessor.PropertyAccessor(type, propertyName);
                            _cachedReflections.Add(typePropertyDefinition, fastProperty);
                        }
                        else
                        {
                            fastProperty = (STSdb.MTL.Utils.Reflection.FastDynamicPropertyAccessor.PropertyAccessor)o;
                        }
                    }
                    finally
                    {
                        _sync_cachedReflections.ExitWriteLock();
                    }
                }
                else
                {
                    fastProperty = (STSdb.MTL.Utils.Reflection.FastDynamicPropertyAccessor.PropertyAccessor)o;
                }
            }
            finally
            {
                _sync_cachedReflections.ExitUpgradeableReadLock();
            }

            return fastProperty;
        }

    }
}
